import processing.opengl.*;

import igeo.p.*;
import igeo.io.*;
import igeo.geo.*;
import igeo.util.*;
import igeo.core.*;
import igeo.gui.*;

int trans = 30;
boolean connect[][];
int numOfNodes = 200;
int numOfLevels = 3;
int nodesOnLevel[] = {numOfNodes, 16,  4,   1};
float radiuss[] =    {300,        200, 100, 0};
float centerX, centerY;
PVector positions[][];
int tree[][];
int routeTables[][][];
float outer[][];
int treeDepth = 3;
static int END = -100;
int numOfRouters;

class Node
{
    public float x;
    public float y;
    public int parent;
}
Node[] nodes;

class Router
{
    public float x;
    public float y;
    public int parent;
    public int[] children;
    public int depth;
    public int lower;
    public int upper;
    public int getNext(int id)
    {
        if (isMine(id)) {
            if (depth == 1) {
                return -1;
            } else {
                for (int i=0; i<4; ++i) {
                    if (routers[children[i]].isMine(id)) 
                        return children[i];
                }
                return -1;
            }
        } else return parent;
    }
    public boolean isMine(int id)
    {
        return id >= lower && id < upper;
    }
    
    public Router(int _lower, int _upper, int _parent, int _depth)
    {
        lower = _lower;
        upper = _upper;
        parent = _parent;
        depth = _depth;
    }
}
Router[] routers;

void generateTree(int id, int lower, int upper, int parent, int depth)
{
    //println("depth " + depth + " id: " + id);
    if (depth == 1) { 
        routers[id] = new Router(lower, upper, parent, depth);
        routers[id].children = new int[upper-lower];
        //println("creating nodes from " + lower + " to " + (upper - 1));
        for (int i=lower; i<upper; ++i) {
            routers[id].children[i-lower] = i;
            nodes[i] = new Node();
            float theta = map(i, 0, numOfNodes, 0, 2 * PI);
            float x = centerX + radiuss[0] * cos(theta);
            float y = centerY + radiuss[0] * sin(theta);
            nodes[i].x = x;
            nodes[i].y = y;
            nodes[i].parent = id;
        }
        return;
    }
    routers[id] = new Router(lower, upper, parent, depth);
    routers[id].children = new int[4];
    
    int part = (upper - lower) / 4; // 4-ary tree
    int [][]sec = new int[4][2];
    for (int i=0; i<4; ++i) {
        sec[i][0] = lower + i * part;
        sec[i][1] = lower + (i + 1) * part;
        if (i == 3) sec[i][1] = upper;
        routers[id].children[i] = id * 4 + i + 1;
    }
    for (int i=0; i<4; ++i) {
        generateTree(routers[id].children[i], sec[i][0], sec[i][1], id, depth - 1);
    }
}

void generateConnection()
{
    connect = new boolean[numOfNodes][numOfNodes];
    for (int i=0; i<numOfNodes; ++i) {
        for (int j=i+1; j<numOfNodes; ++j) {
            float r = random(100);
            boolean bv;
            float bound = 90.f;
            if (i % 2 == 1) bound = 100;
            if (i > 20 && i < 40 && j % 2 == 0) bound = 50;
            if (r > bound) bv = true;
            else bv = false;
            connect[i][j] = connect[j][i] = bv;
        }
    }
}

void preProcess(float[] x, float[] y, int cnt)
{
    float beta = 0.8;
    for (int i=2; i<cnt-2; ++i) {
        float tmpx = (i * (x[cnt-1] - x[0])) / (cnt - 1) + x[0];
        float tmpy = (i * (y[cnt-1] - y[0])) / (cnt - 1) + y[0];
        x[i] = beta * x[i] + (1 - beta) * tmpx;
        y[i] = beta * y[i] + (1 - beta) * tmpy;
    }
}


void connectNodes(int from, int to)
{
    float[] x = new float[10];
    float[] y = new float[10];
    x[0] = outer[from][0];
    y[0] = outer[from][1];
    x[1] = nodes[from].x;
    y[1] = nodes[from].y;
    int cur = nodes[from].parent;
    x[2] = routers[cur].x;
    y[2] = routers[cur].y;
    int cnt = 3;
    while (true) {
        cur = routers[cur].getNext(to);
        if (cur == -1) break;
        x[cnt] = routers[cur].x;
        y[cnt] = routers[cur].y;
        ++cnt;
    }
    x[cnt] = nodes[to].x;
    y[cnt] = nodes[to].y;
    ++cnt;
    x[cnt] = outer[to][0];
    y[cnt] = outer[to][1];
    ++cnt;
    //for (int i=0; i<cnt; ++i) {
    //    println("i " + i + " x: " + x[i] + " y: " + y[i]);
    //}
    //println();
    preProcess(x, y, cnt);
    drawLine(x, y, cnt);
}

void drawLine(float[] x, float[] y, int cnt)
{
    noFill();
    stroke(255, 0, 0, trans);
    //strokeWeight(2);
    smooth();
    
    /*if (cnt < 3) {
        arc(x[0], y[0], 
    }*/
    int sec = 255 / cnt;
    beginShape();
    //stroke(255, 0, 0, trans);
    //curveVertex(x[0], y[0]);
    for (int i=0; i<cnt; ++i) {
        stroke(255-i*sec, i*sec, 0, trans);
        curveVertex(x[i], y[i]);
    }
    //stroke(0, 255, 0, trans);
    //curveVertex(x[cnt-1], y[cnt-1]);
    endShape();
    
}

void setup() 
{
    size(800, 800, OPENGL);
    centerX = width / 2;
    centerY = height / 2;
    generateConnection();
    routers = new Router[25];
    nodes = new Node[numOfNodes];
    generateTree(0, 0, numOfNodes, 0, treeDepth);
    routers[0].x = centerX;
    routers[0].y = centerY;
    for (int i=0; i<4; ++i) {
        float theta = map(i, 0, 4, 0, 2 * PI) + map(1, 0, 4, 0, PI);
        float x = centerX + radiuss[2] * cos(theta);
        float y = centerY + radiuss[2] * sin(theta);
        routers[i+1].x = x;
        routers[i+1].y = y;
    }
    for (int i=0; i<16; ++i) {
        float theta = map(i, 0, 16, 0, 2 * PI) + map(1, 0, 15, 0, PI);
        float x = centerX + radiuss[1] * cos(theta);
        float y = centerY + radiuss[1] * sin(theta);
        routers[i+5].x = x;
        routers[i+5].y = y;
    }
    //traversalTree(0);
    background(255);
    fill(0);
    //for (int i=0; i<21; ++i) {
    //    ellipse(routers[i].x, routers[i].y, 10, 10);
    //}
    outer = new float[numOfNodes][2];
    for (int i=0; i<numOfNodes; ++i) {
        float theta = map(i, 0, numOfNodes, 0, 2 * PI);
        float x = centerX + 320 * cos(theta);
        float y = centerY + 320 * sin(theta);
        outer[i][0] = x;
        outer[i][1] = y;
        ellipse(nodes[i].x, nodes[i].y, 10, 10);
    }
    
    /*
    for (int i=0; i<numOfNodes; ++i) {
        nodes[i] = 
    }*/
    for (int i=0; i<numOfNodes; ++i) {
        for (int j=i+1; j<numOfNodes; ++j) if (connect[i][j]) {
            //println("from " + nodes[i].x + " " + nodes[i].y + " to " + nodes[j].x + " " + nodes[j].y);
            connectNodes(i, j);
        }
    }
}

void draw()
{
}

